/*

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

package com.bigdata.util.config;

import java.io.IOException;

import net.jini.url.httpmd.HttpmdUtil;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * Utility class that provides a set of static convenience methods
 * related to configuration and deployment of the Bigdata services.
 * Although useful in general, the methods in this utility class may be
 * particularly useful when employed from within a Jini configuration
 * file.
 * <p>
 * This class cannot be instantiated.
 */
public class ConfigurationUtil {

    private static final Logger logger = 
        LogUtil.getLog4jLogger( ConfigurationUtil.class );

    /**
     * Concatenates the given <code>String</code> arrays, returning
     * the result in a new <code>String</code> array.
     */
    public static String[] concat(String[] a, String[] b) {
        String[] result = new String[a.length + b.length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }

    /** 
     * Creates and returns a <code>String</code> array consisting of the
     * contents of the given <code>prevArgList</code> and the arguments
     * reference in the given <code>argString</code>. 
     * <p>
     * Note that the contents of the <code>argString</code> parameter is
     * <code>split</code> on the RS (<i>record separator</i>) control
     * character ("\036").
     */
    public static String[] createArgList(String[] prevArgList,
                                         String   argString)
    {
        if(argString.matches("^[ \t]*$")) return prevArgList;

        String[] newArgs = argString.split("\036");
        return concat(prevArgList, newArgs);
    }

    /**
     * Using the given parameters in the appropriate manner, this method
     * constructs and returns a <code>String</code> whose value is a valid
     * Java RMI <i>codebase</i> specification. This method returns a
     * codebase supporting one of two possible protocols: the standard
     * <i>http</i> protocol, or the Jini <i>httpmd</i> protocol; which
     * was created to support codebase integrity when running a remote
     * service configured for secure code downloading. 
     * <p>
     * The <code>mdAlgorithm</code> parameter is used to indicate to
     * this method which type of codebase should be constructed and
     * returned. If <code>null</code> is input to <code>mdAlgorithm</code>,
     * or if it has one of the values "none", "off", or "" (the empty
     * <code>String</code>), then this method will construct and returned
     * a standard HTTP-based codebase; otherwise, an HTTPMD-based codebase
     * will constructed and returned.
     * <p>
     * Note that the <code>name</code> parameter is handled specially by
     * this method. The value input to that <code>String</code> parameter
     * is interpretted in one of two ways by this method:
     * <p><ul>
     *   <li> The name of the local <i>network interface</i> over which the
     *        class server will communicate with clients requesting the
     *        associated service's downloadable classes through the codebase
     *        returned by this method.
     *   <li> The local <i>or remote</i> IP address or name of the <i>host</i>
     *        on which the class server is running and serving the 
     *        associated service's classes.
     * </ul></p>
     * 
     * This method first treats the value of the <code>name</code> parameter
     * as a network interface, attempting to determine that interface's
     * corresponding IP address. Should that attempt fail, then this method
     * then assumes that the caller intended this parameter to be interpretted
     * as an IP address or host name; in which case, this method uses
     * the value of the parameter as the <i>address/host</i> component of
     * codebase being constructed.
     * <p>
     * This method is intended to provide a convenient mechanism for
     * constructing a service's codebase from within a Jini configuration
     * file. To support both development and deployment scenarios, it
     * is important to be able to flexibly construct such codebases from
     * either a local network interface specification or from a
     * specification that represents the name of a remote host running
     * a shared codebase class server on the appropriate network.
     *
     * @param name        <code>String</code> referencing either a network
     *                    interface, IP address, or host name, each of which
     *                    will be used either to determine the appropriate
     *                    value, or as the actual value, for the 
     *                    <i>address/host</i> component of the returned 
     *                    codebase.
     *
     * @param jarFile     <code>String</code> value that is used for the 
     *                    <i>jar</i> component of the returned codebase;
     *                    which references the JAR file containing the
     *                    downloadable classes that make up the desired 
     *                    service codebase.
     *
     * @param port        <code>int</code> value that is used for the 
     *                    <i>port</i> component of the returned codebase;
     *                    which is the port on which the class server will
     *                    listen for download requests from clients of the
     *                    associated service.
     *
     * @param srcRoot     <code>String</code> value that is used when
     *                    computing an HTTPMD-based codebase. The value
     *                    of this parameter references the root directory
     *                    path (or URL of the directory) containing the JAR
     *                    file(s) (or class file(s)) containing the
     *                    downloadable classes that make up the desired
     *                    service codebase. This parameter is typically the
     *                    root directory from which the class server will
     *                    serve the service's <i>downloadable JAR files</i>.
     *                    Note that this parameter is not used in the
     *                    construction of the returned codebase if the
     *                    <code>mdAlgorithm</code> parameter is 
     *                    <code>null</code> or if it has one of the values
     *                    "none", "off", or "" (the empty <code>String</code>).
     *
     * @param mdAlgorithm <code>String</code> value indicating the 
     *                    message digest algorithm to use when constructing
     *                    an HTTPMD-based codebase (for example, "sha",
     *                    "md5", etc.). Note that if this parameter is
     *                    <code>null</code>, then this method will 
     *                    contruct an HTTP-based codebase.
     *
     * @return a <code>String</code> whose value is a valid Java RMI
     *         <i>codebase</i> specification represented in either 
     *         standard <i>http</i> protocol format, or Jini <i>httpmd</i>
     *         protocol format.
     *
     * @throws IOException if an I/O exception occurs while reading data
     *         from the <code>srcRoot</code>.
     *
     * @throws NullPointerException if <code>null</code> is input for
     *         <code>name</code> or <code>jarFile</code>, and/or
     *         <code>null</code> is input for <code>srcRoot</code>
     *         when <code>mdAlgorithm</code> is non-<code>null</code>
     *         and is not equal to "none", "off", or "" (note that if 
     *         <code>mdAlgorithm</code> is <code>null</code> or equal to
     *         "none", "off", or "", then <code>srcRoot</code> can take
     *         any value, including <code>null</code>).
     *
     * @throws IllegalArgumentException if the value input for 
     *         <code>port</code> is negtive.
     */
    public static String computeCodebase(String  name,
                                         String  jarFile,
                                         int     port,
                                         String  srcRoot,
                                         String  mdAlgorithm)
                                                         throws IOException
    {
        if(name == null) throw new NullPointerException("name cannot be null");
        if(jarFile == null) throw new NullPointerException
                                                 ("jarFile cannot be null");
        if(port < 0) throw new IllegalArgumentException
                                                 ("port cannot be negative");
        boolean doHttpmd = true;
        if(    (mdAlgorithm == null) 
            || (("").equals(mdAlgorithm))
            || (("off").equals(mdAlgorithm))
            || (("none").equals(mdAlgorithm)) )
        {
            doHttpmd = false;
        }
        if( doHttpmd && (srcRoot == null) ) {
            throw new NullPointerException
                       ("srcRoot cannot be null when constructing "
                        +"an HTTPMD codebase");
        }

        String codebase = null;
        String ipAddr   = name;

        // Determine if name is a Nic name or a host name 
        try {
            ipAddr = NicUtil.getIpAddress(name);
        } catch(Exception e) {
            // must be a hostname  
            logger.log(Level.TRACE, name+" - not a valid "
                           +"network interface, assuming host name");
        }

        // Construct the codebase, either httpmd or http 
        if(doHttpmd) {
            String httpmdUrl = 
               "httpmd://"+ipAddr+":"+port+"/"+jarFile+";"+mdAlgorithm+"=0";
            codebase = HttpmdUtil.computeDigestCodebase(srcRoot, httpmdUrl);;
        } else {//use httpmd
            codebase = "http://"+ipAddr+":"+port+"/"+jarFile;
        }
        logger.log(Level.TRACE, "codebase = "+codebase);

        return codebase;
    }

    /**
     * Convenience method that ultimately calls the primary 5-argument
     * version of this method. This method allows one to input a
     * <code>String</code> value for the port to use when constructing
     * the codebase; which can be useful when invoking this method
     * from a Jini configuration file.
     *
     * @throws NumberFormatException if the value input for <code>port</code>
     *         does not contain a parsable <code>int</code>.
     */
    public static String computeCodebase(String  name,
                                         String  jarFile,
                                         String  port,
                                         String  srcRoot,
                                         String  mdAlgorithm)
                                                         throws IOException
    {
        return computeCodebase(name, jarFile, Integer.parseInt(port),
                               srcRoot, mdAlgorithm);
    }

    /**
     * Convenient three-argument version of <code>computeCodebase</code>
     * that will always return a codebase <code>String</code> represented
     * in standard <i>http</i> protocol format.
     */
    public static String computeCodebase(String  name,
                                         String  jarFile,
                                         int     port) throws IOException
    {
        return computeCodebase(name, jarFile, port, null, null);
    }

    /**
     * Convenient three-argument version of <code>computeCodebase</code>
     * that takes a <code>String</code> value for the <code>port</code>
     * parameter, always return a codebase <code>String</code> represented
     * in standard <i>http</i> protocol format.
     *
     * @throws NumberFormatException if the value input for <code>port</code>
     *         does not contain a parsable <code>int</code>.
     */
    public static String computeCodebase(String  name,
                                         String  jarFile,
                                         String  port) throws IOException
    {
        return computeCodebase(name, jarFile, Integer.parseInt(port));
    }
}
